<?php

namespace Core\Common\Helper;

use Core\Common\Argus\Interceptor\ArgusInterceptor;
use Core\Common\Route\Request;

/**
 * Description of HttpClient
 *
 * @author baiyang@ibantang.com
 */
class HttpClient
{
    const TIME_OUT = 1;

    /**
     * @param $host
     * @param $uri
     * @param array $data
     * @param array $header
     * @param array $options
     * @return mixed
     * @throws \Exception
     */
    public static function get($host, $uri, $data = [], $header = [], $options = [])
    {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => false,
            CURLOPT_TIMEOUT_MS => ($options['timeout'] ?? self::TIME_OUT) * 1000,
            CURLOPT_URL => self::getUrl($host, $uri, $data),
            CURLOPT_CUSTOMREQUEST => 'GET',
            CURLOPT_HTTPHEADER => [
                'X-Forwarded-For: ' . Request::server('HTTP_X_FORWARDED_FOR'),
                'X-SQKB-FROM-SERVICE: ' . (defined('CORE_NAME') ? CORE_NAME : ''),
                'EXPECT:',
            ],
        ]);
        return self::exec($ch, 'GET', $data, [
            'host' => $host,
            'uri' => $uri,
            'header' => $header,
            'options' => $options
        ]);
    }

    /**
     * @param $host
     * @param $uri
     * @param array $data
     * @param array $header
     * @param array $options
     * @return mixed
     * @throws \Exception
     */
    public static function post($host, $uri, $data = [], $header = [], $options = [])
    {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => false,
            CURLOPT_TIMEOUT_MS => ($options['timeout'] ?? self::TIME_OUT) * 1000,
            CURLOPT_URL => self::getUrl($host, $uri),
            CURLOPT_POSTFIELDS => http_build_query($data),
            CURLOPT_POST => true,
            CURLOPT_HTTPHEADER => [
                'X-Forwarded-For: ' . Request::server('HTTP_X_FORWARDED_FOR'),
                'X-SQKB-FROM-SERVICE: ' . (defined('CORE_NAME') ? CORE_NAME : ''),
                'EXPECT:',
            ],
        ]);
        return self::exec($ch, 'POST', $data, [
            'host' => $host,
            'uri' => $uri,
            'header' => $header,
            'options' => $options
        ]);
    }

    /**
     * @param $host
     * @param $uri
     * @param array $data
     * @param array $header
     * @param array $options
     * @return mixed
     * @throws \Exception
     */
    public static function put($host, $uri, $data = [], $header = [], $options = [])
    {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => false,
            CURLOPT_TIMEOUT_MS => ($options['timeout'] ?? self::TIME_OUT) * 1000,
            CURLOPT_URL => self::getUrl($host, $uri),
            CURLOPT_POSTFIELDS => json_encode($data),
            CURLOPT_CUSTOMREQUEST => 'PUT',
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'X-Forwarded-For: ' . Request::server('HTTP_X_FORWARDED_FOR'),
                'X-SQKB-FROM-SERVICE: ' . (defined('CORE_NAME') ? CORE_NAME : ''),
            ],
        ]);
        return self::exec($ch, 'PUT', $data, [
            'host' => $host,
            'uri' => $uri,
            'header' => $header,
            'options' => $options
        ]);
    }

    /**
     * @param $host
     * @param $uri
     * @param array $data
     * @param array $header
     * @param array $options
     * @return mixed
     * @throws \Exception
     */
    public static function delete($host, $uri, $data = [], $header = [], $options = [])
    {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => false,
            CURLOPT_TIMEOUT_MS => ($options['timeout'] ?? self::TIME_OUT) * 1000,
            CURLOPT_URL => self::getUrl($host, $uri, $data),
            CURLOPT_CUSTOMREQUEST => 'DELETE',
            CURLOPT_HTTPHEADER => [
                'X-Forwarded-For: ' . Request::server('HTTP_X_FORWARDED_FOR'),
                'X-SQKB-FROM-SERVICE: ' . (defined('CORE_NAME') ? CORE_NAME : ''),
            ]
        ]);

        return self::exec($ch, 'DELETE', $data, [
            'host' => $host,
            'uri' => $uri,
            'header' => $header,
            'options' => $options
        ]);
    }
    /**
     * @param $host
     * @param $uri
     * @param array $data
     * @param array $header
     * @param array $options
     * @return mixed
     * @throws \Exception
     */
    public static function postJson($host, $uri, $data = [], $header = [], $options = [])
    {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_HEADER => false,
            CURLOPT_TIMEOUT_MS => ($options['timeout'] ?? self::TIME_OUT) * 1000,
            CURLOPT_URL => self::getUrl($host, $uri),
            CURLOPT_POSTFIELDS => json_encode($data),
            //CURLOPT_VERBOSE => true,
            CURLOPT_CUSTOMREQUEST => 'POST',
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'X-Forwarded-For: ' . Request::server('HTTP_X_FORWARDED_FOR'),
                'X-SQKB-FROM-SERVICE: ' . (defined('CORE_NAME') ? CORE_NAME : ''),
            ],
        ]);
        return self::exec($ch, 'POST', $data, [
            'host' => $host,
            'uri' => $uri,
            'header' => $header,
            'options' => $options
        ]);
    }

    /**
     * @param $ch
     * @param array $header
     */
    private static function setHeader($ch, $header = [])
    {
        if (!$header) {
            return;
        }
        $headers = [];
        foreach ($header as $k => $v) {
            if (is_int($k) || ctype_digit($k)) {
                $headers[] = $v;
            } else {
                $headers[] = $k . ':' . $v;
            }
        }
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    }

    /**
     * @param $host
     * @param $uri
     * @param array $data
     * @return string
     */
    private static function getUrl($host, $uri, $data = [])
    {
        if ('http' != substr($host, 0, 4)) {
            $url = "http://{$host}/" . ltrim($uri, '/');
        } else {
            $url = "{$host}/" . ltrim($uri, '/');
        }
        if ($data) {
            if (strpos($url, '?') !== false) {
                $url .= '&' . http_build_query($data);
            } else {
                $url .= '?' . http_build_query($data);
            }
        }
        return $url;
    }

    /**
     * @param $ch
     * @param $method
     * @param $data
     * @param array $requests
     * @return mixed
     * @throws \Exception
     */
    private static function exec($ch, $method, $data, $requests = [])
    {
        if (Request::isRunningInMars()) {
            $host = $requests['host'] ?? '';
            $hosts = parse_url($host);
            $host = $hosts['host'] ?? '';
            if (!filter_var($host, FILTER_VALIDATE_IP) && strpos($host, "sqkb") === false && strpos($host, "17gwx") === false) {
                \Core\Common\Base\Log::save('mars_curl', json_encode([$method, $data, $requests]));
                curl_close($ch);
                return json_encode([]);
            }
        }

        $spanId = Request::nextSpanId();
        $headers = [
            'X_SQKB_TRACE_ID' => Request::server('HTTP_X_SQKB_TRACE_ID'),
            'X_SQKB_ARGUS_IS_SAMPLE' => Request::server('HTTP_X_SQKB_ARGUS_IS_SAMPLE'),
            'X_SQKB_ARGUS_SPAN_ID' => $spanId,
            'X-SQKB-FROM-SERVICE' => defined('CORE_NAME') ? CORE_NAME : '',
            'Expect' => '',
        ];
        self::setHeader($ch, array_merge($headers, $requests['header']));

        if (Request::isArgusSimple()) {
            $requests['param'] = $data;
            $requests['method'] = $method;
            ArgusInterceptor::handleHttpExec('cs', $spanId, $requests);
        }

        $response = curl_exec($ch);
        // https://www.php.net/manual/en/function.curl-getinfo.php
        $info = curl_getinfo($ch);
        $data = [
            'url' => $info['url'],
            'response_code' => $info['http_code'],
            'namelookup_time' => $info['namelookup_time'],
            'connect_time' => $info['connect_time'],
            'total_time' => $info['total_time'],
            'content_length' => $info['download_content_length'],
        ];
        if (Request::isArgusSimple()) {
            ArgusInterceptor::handleHttpExec('cr', $spanId, $data);
        }

        if (empty($response)) {
            $errorInfo = curl_error($ch);
            $errorNo = curl_errno($ch);
            curl_close($ch);

            if (!empty($errorInfo)) {
                $inspectInfo = json_encode($info);
                throw new \Exception($errorInfo . "; curl debug: " . $inspectInfo, $errorNo);
            }

            if (!empty($errorNo)) {
                $inspectInfo = json_encode($info);
                throw new \Exception('curl error_no:' . $errorNo . '; curl debug:' . $inspectInfo, $errorNo);
            }
        }

        curl_close($ch);
        return $response;
    }
}
